/******************************************************************************
 * This file is part of ZSTRING.
 *
 * ZSTRING is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ZSTRING is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ZEMB.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: zstring
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://gitee.com/newgolo/zstring.git
 * Copyright 2022~2025 @ ShenZhen ,China
 *******************************************************************************/
#include "zstring.h"

#ifdef __cplusplus
extern "C" {
#endif
#define REALLOC_SIZE (5) /* realloc increment or decrement size: 2^5 */
const int ZString_npos = -1;
unsigned char z_debug_enable = 0;
typedef struct ZStringHandler {
    ZString m_object;
    char* m_data;
    int m_size;
    int m_allocated;
} ZStringHandler;

void zdebug_set(int debug) {
    z_debug_enable = !!debug;
}

ZString* ZString_create() {
    ZStringHandler* handler = (ZStringHandler*)MALLOC(sizeof(ZStringHandler));
    if (!handler) {
        PRINT("%s(L%d): ZString memory allocate exception!\n", __FILE__, __LINE__);
        EXIT(-1);
    }
    handler->m_data = (char*)MALLOC(1);
    if (!handler->m_data) {
        FREE_SAFE(handler);
        PRINT("%s(L%d): ZString memory allocate exception!\n", __FILE__, __LINE__);
        EXIT(-1);
    }
    handler->m_size = 0;
    handler->m_data[handler->m_size] = '\0';
    handler->m_allocated = 0;
    handler->m_object.thiz = &handler->m_object;
    PRINT_DBG("ZString create object: %p", handler);
    return &handler->m_object;
}

ZString* ZString_createWithStr(const char* str) {
    return ZString_createWithData(str, strlen(str));
}

ZString* ZString_createWithData(const char* data, int size) {
    ZStringHandler* handler = (ZStringHandler*)ZString_create();
    int prealloc = 0;
    if (size > 0) {
        prealloc = ((size + (1 << REALLOC_SIZE)) >> REALLOC_SIZE) << REALLOC_SIZE;
        handler->m_data = (char*)REALLOC(handler->m_data, prealloc);
        if (!handler->m_data) {
            PRINT("%s(L%d): ZString memory allocate exception!\n", __FILE__, __LINE__);
            EXIT(-1);
        }
    }
    MEMCPY(handler->m_data, data, size);
    handler->m_size = size;
    handler->m_data[size] = '\0';
    handler->m_allocated = prealloc;
    handler->m_object.thiz = &handler->m_object;
    return &handler->m_object;
}

ZString* ZString_clone(ZString* thiz) {
    ZString* object = ZString_create();
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    if (handler->m_size > 0) {
        ZString_appendData(object, handler->m_data, handler->m_size);
    }
    return object;
}

void ZString_destroy(ZString* object) {
    if (object) {
        ZStringHandler* handler = (ZStringHandler*)(object);
        PRINT_DBG("ZString destroy object: %p", object);
        FREE_SAFE(handler->m_data);
        handler->m_data = NULL;
        FREE_SAFE(handler);
    }
}

char* ZString_data(ZString* thiz) {
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    return handler->m_data;
}

int ZString_size(ZString* thiz) {
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    return handler->m_size;
}

char ZString_at(ZString* thiz, int pos) {
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    if (pos >= handler->m_size) {
        PRINT("%s(L%d): ZString position out of range!\n", __FILE__, __LINE__);
        EXIT(-1);
    }
    return handler->m_data[pos];
}

void ZString_clear(ZString* thiz) {
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    handler->m_data = (char*)REALLOC(handler->m_data, 1);
    if (!handler->m_data) {
        PRINT("%s(L%d): ZString memory allocate exception!\n", __FILE__, __LINE__);
        EXIT(-1);
    }
    handler->m_size = 0;
    handler->m_data[handler->m_size] = '\0';
    handler->m_allocated = 0;
}

int ZString_find(ZString* thiz, int pos, const char* findStr, int size) {
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    int count = handler->m_size;
    int offset = pos;
    char* find = MEMCHR(handler->m_data + offset, findStr[0], count - offset);
    if (!find) {
        return ZString_npos;
    }
    for (;;) {
        if (MEMCMP(find, findStr, size) == 0) {
            return find - handler->m_data;
        }
        offset = find - handler->m_data + 1;
        find = MEMCHR(handler->m_data + offset, findStr[0], count - offset);
        if (!find) {
            return ZString_npos;
        }
    }
    return ZString_npos;
}

void ZString_appendCh(ZString* thiz, char ch) {
    ZString_appendData(thiz, &ch, 1);
}

void ZString_appendStr(ZString* thiz, const char* str) {
    ZString_appendData(thiz, str, strlen(str));
}

void ZString_appendData(ZString* thiz, const char* data, int size) {
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    ZStringHandler* dataHandler = (ZStringHandler*)ZString_createWithData(data, size);
    if ((handler->m_size + size + 1) > handler->m_allocated) {
        int prealloc = ((handler->m_size + size + (1 << REALLOC_SIZE)) >> REALLOC_SIZE) << REALLOC_SIZE;
        handler->m_data = (char*)REALLOC(handler->m_data, prealloc);
        if (!handler->m_data) {
            ZString_destroy((ZString*)dataHandler);
            PRINT("%s(L%d): ZString memory allocate exception!\n", __FILE__, __LINE__);
            EXIT(-1);
        }
        handler->m_allocated = prealloc;
    }

    MEMCPY(handler->m_data + handler->m_size, dataHandler->m_data, dataHandler->m_size);
    handler->m_size += size;
    handler->m_data[handler->m_size] = '\0';
    ZString_destroy((ZString*)dataHandler);
    // PRINT_DBG("size:%d, allocated:%d", handler->m_size, handler->m_allocated);
}

void ZString_append(ZString* thiz, const ZString* object) {
    ZStringHandler* handler = (ZStringHandler*)(object);
    ZString_appendData(thiz, handler->m_data, handler->m_size);
}

int ZString_erase(ZString* thiz, int pos, int count) {
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    MEMMOVE(handler->m_data + pos, handler->m_data + pos + count, handler->m_size - pos - count);
    handler->m_size -= count;
    int prealloc = 0;
    if (handler->m_size > 0) {
        prealloc = ((handler->m_size + (1 << REALLOC_SIZE)) >> REALLOC_SIZE) << REALLOC_SIZE;
    }
    if (prealloc != 0 && prealloc != handler->m_allocated) {
        handler->m_data = (char*)REALLOC(handler->m_data, prealloc);
        if (!handler->m_data) {
            PRINT("%s(L%d): ZString memory allocate exception!\n", __FILE__, __LINE__);
            EXIT(-1);
        }
    }
    handler->m_allocated = prealloc;
    handler->m_data[handler->m_size] = '\0';
    // PRINT_DBG("size:%d, allocated:%d", handler->m_size, handler->m_allocated);
    return pos;
}

int ZString_insert(ZString* thiz, int pos, char ch) {
    ZString_insertData(thiz, pos, &ch, 1);
    return pos;
}

void ZString_insertData(ZString* thiz, int pos, const char* data, int size) {
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    ZStringHandler* dataHandler = (ZStringHandler*)ZString_createWithData(data, size);
    if ((handler->m_size + size) >= handler->m_allocated) {
        int prealloc = ((handler->m_size + size + (1 << REALLOC_SIZE)) >> REALLOC_SIZE) << REALLOC_SIZE;
        handler->m_data = (char*)REALLOC(handler->m_data, prealloc);
        if (!handler->m_data) {
            ZString_destroy((ZString*)dataHandler);
            PRINT("%s(L%d): ZString memory allocate exception!\n", __FILE__, __LINE__);
            EXIT(-1);
        }
        handler->m_allocated = prealloc;
    }
    MEMMOVE(handler->m_data + pos + size, handler->m_data + pos, handler->m_size - pos);
    MEMCPY(handler->m_data + pos, dataHandler->m_data, size);
    handler->m_size += size;
    handler->m_data[handler->m_size] = '\0';
    ZString_destroy((ZString*)dataHandler);
    // PRINT_DBG("size:%d, allocated:%d", handler->m_size, handler->m_allocated);
}

void ZString_insertString(ZString* thiz, int pos, const ZString* object) {
    ZStringHandler* handler = (ZStringHandler*)(object);
    ZString_insertData(thiz, pos, handler->m_data, handler->m_size);
}

void ZString_hexViewer(ZString* thiz, char* tag) {
    int i;
    ZStringHandler* handler = (ZStringHandler*)(thiz);
    if (tag) {
        PRINT("%s[", tag);
    } else {
        PRINT("ZHex[");
    }
    for (i = 0; i < handler->m_size; ++i) {
        PRINT("%02X ", (unsigned char)(handler->m_data[i]));
    }
    PRINT("]\n");
}
#ifdef __cplusplus
}
#endif